use koopa::ir::{BinaryOp, ValueKind};

pub trait GenerateAsm {
  fn generate(&self) -> String {
    String::from("")
  }
  fn dump_asm(&self, _func: &koopa::ir::FunctionData) -> String {
    String::from("")
  }
}

impl GenerateAsm for koopa::ir::Program {
  fn generate(&self) -> String {
    // prologue
    let mut asm = String::from(
      r#"  .text
  .globl main
"#,
    );

    for &func in self.func_layout() {
      asm = asm + &self.func(func).generate();
    }
    asm
  }
}

impl GenerateAsm for koopa::ir::FunctionData {
  fn generate(&self) -> String {
    // get function name
    let func_name = self.name().strip_prefix('@').unwrap();
    let mut asm = format!("{}:", func_name);

    for (&_bb, node) in self.layout().bbs() {
      for &inst in node.insts().keys() {
        // for every Value in function
        asm = asm + &self.dfg().value(inst).dump_asm(self);
      }
    }
    asm
  }
}

impl GenerateAsm for koopa::ir::entities::ValueData {
  fn dump_asm(&self, func: &koopa::ir::FunctionData) -> String {
    // for all ValueKind lists
    // please check https://docs.rs/koopa/latest/koopa/ir/entities/enum.ValueKind.html
    println!("{:#?}", self.kind());
    match self.kind() {
      ValueKind::Integer(int) => {
        format!("{}", int.value())
      }
      ValueKind::Binary(bin) => {
        let op: String;
        match bin.op() {
          BinaryOp::Eq => {
            op = String::from("eq");
          }
          BinaryOp::Sub => {
            op = String::from("sub");
          }
          _ => unreachable!()
        }
        let lhs_value = func.dfg().value(bin.lhs()).dump_asm(func);
        let rhs_value = func.dfg().value(bin.rhs()).dump_asm(func);
        format!(" {} {} {} ", lhs_value, op, rhs_value)
      }
      ValueKind::Return(ret) => {
        let ret = ret.value().unwrap();
        let ret_value = func.dfg().value(ret).dump_asm(func);
        format!(
          r#"
  li a0, {}
  ret"#,
          ret_value
        )
      }
      _ => unreachable!(),
    }
  }
}
